#ifndef DATA_POOL_H__
#define DATA_POOL_H__

#include <vector>
#include <memory>
#include <queue>
#include <assert.h>


template<typename T>
class inclosure_elem;

template<typename T>
class pool_{
    
public:
    virtual ~pool_(){}
    virtual inclosure_elem<T> get() = 0;
};


/**
 * 要求：T::POOL_SIZE
 *      T::clear_data(std::shared_ptr<T> data);
 */
template<typename T>
class data_pool: public pool_<T>{
    friend class inclosure_elem<T>;
private:
        std::vector<std::shared_ptr<T>> pool; //数据指向池
        size_t ready_index; //记录下一个可用的索引
        size_t ready_index_of_null; //记录下一个需要被填充的索引, 没有坑了就是pool.size()

        std::shared_ptr<T> get_instance_of_data(){
            if(ready_index_of_null == ready_index){
                return nullptr; //pool 已经分配完了....
            }else
            if(ready_index == pool.size()){
                ready_index = 0;
            }
            std::shared_ptr<T> instance = pool[ready_index];
            pool[ready_index] = nullptr;
            ++ready_index;
            return instance;
        }

        void set_recycle(std::shared_ptr<T> recycle_data){
            if(ready_index_of_null == pool.size())
                ready_index_of_null = 0;
                
            pool[ready_index_of_null++] = recycle_data;
        }
public:
    explicit data_pool(unsigned int size_):pool(size_), ready_index(0), ready_index_of_null(size_){
        pool.resize(size_);

        for(size_t i = 0; i < pool.size(); ++i){
            pool[i] = std::make_shared<T> ();
        }
    }

    inclosure_elem<T> get() override{
        return get_instance_of_data();
    }
    
    static data_pool<T>& getInstance(){
        static data_pool<T> *instance = nullptr;
        if(!instance){
            instance = new data_pool<T>(T::POOL_SIZE);
        }

        return *instance;
    }
};


template<typename T>
class inclosure_elem{
    friend class data_pool<T>;
private:
    std::shared_ptr<T> data;

    inclosure_elem(std::shared_ptr<T> data_):data(data_){

    }

    void change(inclosure_elem& other){
        data = other.data;
        other.data = nullptr;
    }
public:
    ~inclosure_elem(){
        clear();
    }

    inclosure_elem(inclosure_elem&& other){
        change(other);
    }

    inclosure_elem& operator=(inclosure_elem&& other){
        clear();
        change(other);
        return *this;
    }

    T* operator->(){
        return data.get();
    }

    T& operator*(){
        return *data;
    }

    operator bool()const{
        return bool(data);
    }

    void clear(){
        if(data != nullptr){
            T::clear_data(data);
            data_pool<T>::getInstance().set_recycle(data);
            data = nullptr;
        }
    }
};

#endif //DATA_POOL_H__